热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

可能会|也就是_手把手教你从Java8升级到Java11

篇首语:本文由编程笔记#小编为大家整理,主要介绍了手把手教你从Java8升级到Java11相关的知识,希望对你有一定的参考价值。 点击上方 终端研发部,选择

篇首语:本文由编程笔记#小编为大家整理,主要介绍了手把手教你从Java8升级到Java11相关的知识,希望对你有一定的参考价值。


 点击上方 终端研发部,选择 设为星标


每天9:30点,干货准时奉上!


来源 | blog.csdn.net/weixin_39901203/article/details/110507366
原文 | blog.codefx.org/java/java-11-migration-guide/

一些背景


在背景知识,我们会讨论一些关于新的JDK Release周期,OpenJDK特性归一化,LTS(Long-term support长期支持版本)的事情。


1. 新的发布周期


这个就可以长话短说了,反正我们知道如下两点就好:


  • 每六个月发布一个大更新(就是每年的3月还有9月)

  • 对于每个大版本更新,会有两次小版本更新(在发布后一个月或者四个月之后)


2. OpenJDK已可以作为新的线上标准JDK


在2018.9之前,Oracle JDK是大家普遍运用于线上的JDK,OpenJDK的特性并不完全,并且Oracle JDK号称做了很多优化。在2018.9之后,Oracle JDK正式商用(开发不收费,但是运行线上业务收费)。


但是与此同时,Oracle宣布,OpenJDK与Oracle JDK在功能上不会有区别。并且,OpenJDK 11 RTS将会由红帽社区进行维护。这样,更加增加了可靠性与保证问题的及时解决。


我们可以在线上使用OpenJDK,开发时,使用任意的JDK。


3. LTS(Long-term support长期维护)版本


对于商业版的JDK,不同的厂商都将长期维护版本定在JDK 11/17/23/...


对于OpenJDK,社区说,对于这些版本,至少会提供四年的维护更新时间。每个长期维护版本都会有一个固定的管理者,对于OpenJDK11,应该就是红帽社区。现在源代码搞定了,但是,我们应该从哪里获取编译好的OpenJDK呢?这个可以交给AdoptOpenJDK,它会一直收集不同版本的OpenJDK以及全平台的build好的OpenJDK


4. Amazon Corretto


AWS也提供了自己的OpenJDK,Amazon Corretto:


  • 基于OpenJDK,采取GPL+CE协议,做了一些安全性,性能和稳定性优化,并且修复了一些bug

  • 支持linux,MAC OS还有Windows操作系统

  • 长期支持Java 8并且至少到2023年

  • 从2019年开始支持Java 11并且至少到2024

  • 季度更新,并且伴随一些紧急bug修复的更新


OpenJDK社区的FAQ部分曾经提到:“Amazon从2017年开始贡献OpenJDK,并且计划开始大量贡献”。我猜Amazon会把他们在Corretto上面做的优化,合并到OpenJDK源码中,即使没有,Corretto也是开源的,迟早会有人参考并在OpenJDK源码上进行修改。同时也说明,OpenJDK的更新也会及时被合并到Corretto中。


准备迁移


1. 更新好开发环境以及编译环境


各种常用工具,建议升级到如下版本以后:


  • IntelliJ IDEA: 2018.2

  • Eclipse: Photon 4.9RC2 with Java 11 plugin

  • Maven: 3.5.0

  • Maven compiler plugin: 3.8.0

  • surefire and failsafe: 2.22.0

  • Gradle: 5.0


对于如下工具,由于已经不再维护,需要替换成其他工具:


  • FindBugs(静态代码bug发现):用SpotBugs替换。

  • Cobertura(代码测试覆盖率):用Jacoco替换


同时由于在Java 9 之后,每六个月bytecode level会提升一次。如果你依赖的库有处理字节码相关的库,应该注意下版本升级,例如:


对于直接操作字节码的库,如果你升级了JDK,那么最好也跟着升级这些库:ASM (7.0), Byte Buddy (1.9.0), cglib (3.2.8), or Javassist (3.23.1-GA).这些版本是OpenJDK11适用的版本


如果你使用的库依赖了上面提到的操作字节码的库,那么也需要注意下版本依赖,看依赖的操作字节码的库是否升级到了上面提到的版本。对于Spring,最好采用5.1以后的版本, Mockito则是2.20.0以后的版本


2. 引入JPMS后,相关的迁移工作


2.1. Java EE相关模块默认不在Java包里面了,相关的类需要增加额外依赖或者替换成其他的类

如果你的项目中使用了这些类,那么在编译阶段就会报错,例如:


error: package javax.xml.bind does not exist
import javax.xml.bind.JAXBException;

如果你是用JDK 8编译成功,拿到JDK 11运行,就会报错:


Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
    at monitor.Main.main(Main.java:27)
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
    ... 1 more

以下是相关移除列表还有解决方案


JavaBeans Activation Framework (JAF) (javax.activation)变成了一个独立的框架,maven依赖:



    com.sun.activation
    javax.activation
    1.2.0

CORBA(java.corba)在JEP 230已经不复存在了,在你的项目中如果遇到,证明你的项目太古老了。移除掉吧


JTA (java.transaction)变成了独立依赖:



    javax.transaction
    javax.transaction-api
    1.2

JAXB和JAX-WS:



    javax.xml.bind
    jaxb-api
    2.2.8


    com.sun.xml.bind
    jaxb-core
    2.2.8


    com.sun.xml.bind
    jaxb-impl
    2.2.8


    com.sun.xml.ws
    jaxws-ri
    2.3.0
    pom

Common Annotations:



    javax.annotation
    javax.annotation-api
    1.3.1

一个建议就是,在你的项目中如果没有冲突,建议都加上这些依赖。


2.2. 模块可见性导致的内部API不能调用的问题

在Java9之后引入了模块化的概念,是将类型和资源封装在模块中,并仅导出其他模块要访问其公共类型的软件包。如果模块中的软件包未导出或打开,则表示模块的设计人员无意在模块外部使用这些软件包。


这样的包可能会被修改或甚至从模块中删除,无需任何通知。如果仍然使用这些软件包通过使用命令行选项导出或打开它们,可能会面临破坏应用程序的风险!


对于这种限制,在编译阶段,可能会有类似下面的报错:


error: package com.sun.imageio.plugins.jpeg is not visible
import com.sun.imageio.plugins.jpeg.JPEG;
                              ^
  (package com.sun.imageio.plugins.jpeg is declared
  in module java.desktop, which does not export it)

如果是反射的调用,可能在运行阶段有类似于如下的报警:


WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by j9ms.internal.JPEG
    (file:...) to field com.sun.imageio.plugins.jpeg.JPEG.TEM
WARNING: Please consider reporting this
    to the maintainers of j9ms.internal.JPEG
WARNING: Use --illegal-access=warn to enable warnings
    of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
# here's the reflective access to the static field com.sun.imageio.plugins.jpeg.JPEG.TEM

对于这种错误,我们最好是更换API,如果难以实现,则可以通过添加编译以及启动参数解决。


我们需要的参数是:


--add-exports选项:模块声明中的exports语句将模块中的包导出到所有或其他模块,因此这些模块可以使用该包中的公共API。


如果程序包未由模块导出,则可以使用-add-exports的命令行选项导出程序包:


--add-exports /=

如果设置target-module-list为ALL-UNNAMED,那么所有Classpath下的module,都可以访问source-module中的pakage包下的公共API


--add-opens选项: 模块声明中的opens语句使模块里面的包对其他模块开放,因此这些模块可以在运行期使用深层反射访问该程序包中的所有成员类型。


如果一个模块的包未打开,可以使用--add-opens命令行选项打开它。其语法如下:


--add-opens /=

如果设置target-module-list为ALL-UNNAMED,那么所有Classpath下的module,都可以访问source-module中的pakage包下的所有成员类型


对于编译阶段,也就是javac命令,我们只需要添加--add-exports,对于上面的例子,就是:


javac --add-exports java.desktop/com.sun.imageio.plugins.jpeg?s=#61;ALL-UNNAMED

对于运行阶段,也就是java命令,我们最好把--add-exports和--add-open都加上,对于上面的例子,就是:


java --add-exports java.desktop/com.sun.imageio.plugins.jpeg?s=#61;ALL-UNNAMED --add-open java.desktop/com.sun.imageio.plugins.jpeg?s=#61;ALL-UNNAMED

这样,在运行阶段,首先不会有禁止访问报错,同时也不会有警告。


同时,为了在运行期能找到所有需要添加的模块和包,可以通过添加--illegal-access=$value来检查。这个value可以填写:


  • permit: 未来可能会移除。仅在第一次反射调用内部api的时候报警

  • warn:每次次反射调用内部api的时候报警

  • debug:在warn的基础上,加上堆栈输出

  • deny: 拒绝所有非法反射访问内部api


我们可以设置--illegal-access=deny来知道我们需要添加的所有--add-export和--add-open包。


2.3 通过JDK11内置jdeps工具查找过期以及废弃API以及对应的替换

jdeps --jdk-internals -R --class-path 'libs/*' $project

libs是你的所有依赖的目录,$project是你的项目jar包,示例输出:


...
JDK Internal API                         Suggested Replacement
----------------                         ---------------------
sun.misc.BASE64Encoder                   Use java.util.Base64 @since 1.8
sun.reflect.Reflection                   Use java.lang.StackWalker @since 9

在这里简单提一些在JDK11过期,但是JDK8使用的API:


  • sun.misc.Base64 (替换成 java.util.Base64)

  • com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel (替换成 javax.swing.plaf.nimbus.NimbusLookAndFeel)

  • java.util.LogManager, java.util.jar.Pack200.Packer类 Unpacker: addPropertyChangeListener 和 removePropertyChangeListener这两个方法已经移除

  • java.lang.Runtime类: methods getLocalizedInputStream 和 getLocalizedOutputStream方法已经移除

  • SecurityManager的操作方法已经整体移除


2.4. ClassLoader变化带来的URLClassLoader的变化

Java 8的ClassLoader流程:


  • bootstrap classloader加载rt.jar,jre/lib/endorsed

  • ext classloader加载jre/lib/ext

  • application classloader加载-cp指定的类


java9及之后的classloader流程:


  • bootstrap classloader加载lib/modules

  • ext classloader更名为platform classloader,加载lib/modules

  • application classloader加载-cp,-mp指定的类


同时,我们注意到,JDK9开始,AppClassLoader他爹不再是 URLClassLoader


一般热部署,插件部署,都会使用到AppClassLoader,例如Spring-Boot的热部署,老版本的会报异常:


Exception in thread "main" java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
    at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getUrls(DefaultRestartInitializer.java:93)
    at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getInitialUrls(DefaultRestartInitializer.java:56)
    at org.springframework.boot.devtools.restart.Restarter.(Restarter.java:140)
    at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:546)
    at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:67)
    at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:45)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:122)
    at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:69)
    at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:48)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:292)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
    at com.asofdate.AsofdateMain.main(AsofdateMain.java:18)

这是主要是因为AppClassLoader不再是URLClassLoader的子类导致的。


之前对于动态加载的类,我们总是通过将这个类通过反射调用URLClassLoader加到classpath里面进行加载。这么加载在JDK11中已经无法实现,并且这样加载的类不能卸载。


对于动态加载的类,我们在OpenJDK11中只能自定义类加载器去加载,而不是通过获取APPClassLoader去加载。同时,这么做也有助于你随时能将动态加载的类卸载,因为并没有加载到APPClassLoader。


建议使用自定义的类加载器继承SecureClassLoader去加载类:java.security.SecureClassLoader


最后,如果你想访问classpath下的内容,你可以读取环境变量:


String pathSeparator = System
    .getProperty("path.separator");
String[] classPathEntries = System
    .getProperty("java.class.path")
    .split(pathSeparator);

2.5. 过期启动参数修改

JDK 8 到JDK 11有很多参数变化,可以总结为两类参数的变化,一是GC相关的(GC配置调优更加简单),二是日志相关的,日志统一到了一起,不像之前那么混乱


具体请参考:


  • https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-4856361B-8BFD-4964-AE84-121F5F6CF111

  • https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111

  • https://docs.oracle.com/javase/10/tools/java.htm#GUID-3B1CE181-CD30-4178-9602-230B800D4FAE__REMOVEDJAVAOPTIONS-A4E6F213


每个说明参考三部分:


  • Obsolete Java Options: 参数可以被接受但是忽略掉了,但是会有警告,一般这种参数有替代写法,请用替代的写法

  • Deprecated Java Options: 参数可以被接受并有效,但是会有警告,一般这种参数有替代写法,请用替代的写法

  • Removed Java Options: 参数被移除,并且使用的话会有报错


3. 一些框架的OpenJDK11兼容问题持续收集(持续更新中)


  • OpenJDK11与Spring Cloud Finchley的不兼容问题与解决: https://blog.csdn.net/zhxdick/article/details/102314886

  • Lombok编译异常: 升级到1.18.+的版本才可以

  • Spring Cloud Hystrix ThreadPool的bug:Spring Cloud Hystrix ThreadPool的bug



今日好文推荐


GitHub上非常实用的40个开源JAVA项目


XShell收费太贵?快试试开源的NuShell,好用!


GET 和 POST请求的本质区别是什么?看完觉得自己太无知了...


MyBatis批量插入数据你还在用foreach?你们的服务器没崩?



点个在看少个 bug 👇


推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • win10系统搭建Java开发环境的操作方法
    本文介绍了win10系统搭建Java开发环境的详细操作方法,包括下载Windows10系统和Java SE,安装Java开发环境,设置变量等步骤。操作简单,只需按照指导进行即可。 ... [详细]
  • Oracle分析函数first_value()和last_value()的用法及原理
    本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
  • 本文介绍了在Oracle数据库中创建序列时如何选择cache或nocache参数。cache参数可以提高序列的存取速度,但可能会导致序列丢失;nocache参数可以避免序列丢失,但在高并发访问时可能导致性能问题。文章详细解释了两者的区别和使用场景。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 众筹商城与传统商城的区别及php众筹网站的程序源码
    本文介绍了众筹商城与传统商城的区别,包括所售产品和玩法不同以及运营方式不同。同时还提到了php众筹网站的程序源码和方维众筹的安装和环境问题。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
author-avatar
爱情只有确定键没有取消键_874
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有